package org.jflame.commons.excel;

import org.jflame.commons.convert.CalendarToStringConverter;
import org.jflame.commons.convert.ConvertException;
import org.jflame.commons.convert.Converter;
import org.jflame.commons.convert.ConverterRegistry;
import org.jflame.commons.convert.DateToStringConverter;
import org.jflame.commons.convert.NumberToStringConverter;
import org.jflame.commons.convert.ObjectToStringConverter;
import org.jflame.commons.convert.StringToDateConverter;
import org.jflame.commons.convert.StringToNumberConverter;
import org.jflame.commons.convert.StringToTemporalConverter;
import org.jflame.commons.convert.TemporalToStringConverter;
import org.jflame.commons.convert.mutual.DateAndLocalDateConverter;
import org.jflame.commons.convert.mutual.DateAndLocalDateTimeConverter;
import org.jflame.commons.excel.convertor.BoolToTextConverter;
import org.jflame.commons.excel.convertor.DoubleToNumberConverter;
import org.jflame.commons.excel.convertor.KeyValuePairToTextConverter;
import org.jflame.commons.model.pair.IIntKeyPair;
import org.jflame.commons.util.NumberHelper;
import org.jflame.commons.util.StringHelper;

import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.Temporal;
import java.util.Calendar;
import java.util.Date;
import java.util.Optional;

import org.apache.commons.lang3.ClassUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;

@SuppressWarnings("rawtypes")
public final class ExcelConvertUtils {

    private static ConverterRegistry defaultWriterConverters = new ConverterRegistry();
    private static ConverterRegistry defaultReaderConverters = new ConverterRegistry();

    static {
        defaultWriterConverters.addConverter(new CalendarToStringConverter());
        defaultWriterConverters.addConverter(new BoolToTextConverter());
        defaultWriterConverters.addConverter(new KeyValuePairToTextConverter(), IIntKeyPair.class, String.class);
        defaultWriterConverters.addConverter(new ObjectToStringConverter<Object>(), Object.class, String.class);

        ObjectToStringConverter<Boolean> boolStrConverter = new ObjectToStringConverter<>();
        defaultReaderConverters.addConverter(boolStrConverter, Boolean.class, String.class);
        defaultReaderConverters.addConverter(boolStrConverter, boolean.class, String.class);
    }

    /**
     * 使用指定名称转换器，将java属性值转为excel单元格值,未找到转换器返回toString()
     * 
     * @param property ExcelColumnProperty
     * @param value excel单元格值
     * @throws ConvertException 转换器未找到或转换异常
     * @return 转换后字符串
     */
    @SuppressWarnings({ "unchecked" })
    public static <S extends Object> String convertToCellValue(final ExcelColumnProperty property, final S value) {
        if (value instanceof String) {
            return (String) value;
        }
        if (property.getWriteConverter() != null) {
            return ((Converter<S,String>) property.getWriteConverter()).convert(value);
        } else {
            Converter<S,String> converter = (Converter<S,String>) getDefaultWriteConverter(
                    property.getPropertyDescriptor()
                            .getPropertyType(),
                    property.getFmt());
            property.setWriteConverter(converter);// 转换器设置到ExcelColumnProperty,下一行数据直接用
            return converter.convert(value);
        }
    }

    /**
     * 转换excel单元格值到java属性
     * 
     * @param property ExcelColumnProperty
     * @param cell excel单元格
     * @return java属性值
     */
    @SuppressWarnings("unchecked")
    public static Object extractValueFromCell(final ExcelColumnProperty property, final Cell cell) {
        Object cellValue = getCellValue(cell);
        if (cellValue == null) {
            return cellValue;
        }
        Class<?> valueClazz = cellValue.getClass();
        Class<?> needClazz = property.getPropertyDescriptor()
                .getPropertyType();
        // 从excel读取到的数据类型与要求的类型相同不需转换直接返回
        if (valueClazz == needClazz
                || (needClazz.isPrimitive() && valueClazz == ClassUtils.primitiveToWrapper(needClazz))) {
            return cellValue;
        }

        try {
            if (property.getReadConverter() != null) {
                return property.getReadConverter()
                        .convert(cellValue);
            } else {
                Converter readConverter = getDefaultReadConverter(valueClazz, needClazz, property);
                property.setReadConverter(readConverter);

                return readConverter.convert(cellValue);
            }
        } catch (ClassCastException e) {
            throw new ConvertException(e);
        }

    }

    /**
     * 获取excel读取的值转为java属性的默认类型转换器
     * 
     * @param cellValueClazz 读取到的excel值类型,请看{@link #getCellValue}
     * @param fieldClazz 目标属性类型
     * @param fmt
     * @return
     */
    @SuppressWarnings("unchecked")
    private static Converter getDefaultReadConverter(Class<?> cellValueClazz, Class<?> fieldClazz,
            final ExcelColumnProperty property) {
        String fmt = property.getFmt();
        /* if (cellValueClazz == Boolean.class || cellValueClazz == boolean.class) {
            if (fieldClazz == String.class) {
                return new ObjectToStringConverter<Boolean>();
            }
        }*/ if (cellValueClazz == Double.class || cellValueClazz == double.class) {
            if (fieldClazz == String.class) {
                if (StringHelper.isNotEmpty(fmt)) {
                    DecimalFormat numFormator = new DecimalFormat(fmt);
                    return new NumberToStringConverter(numFormator);
                } /*else {//注册器里有
                    NumberToStringConverter converter = new NumberToStringConverter();
                    return converter;
                  }*/
            } else if (Number.class.isAssignableFrom(fieldClazz)) {
                return new DoubleToNumberConverter(fieldClazz);
            }
        } else if (cellValueClazz == String.class) {
            if (NumberHelper.isNumberType(fieldClazz)) {
                return new StringToNumberConverter((Class<Number>) fieldClazz);
            } else if (Date.class.isAssignableFrom(fieldClazz)) {
                return new StringToDateConverter(fieldClazz, fmt);
            } else if (Temporal.class.isAssignableFrom(fieldClazz)) {
                return new StringToTemporalConverter((Class<Temporal>) fieldClazz, fmt);
            } /*else if (fieldClazz == Boolean.class || fieldClazz == boolean.class) {
                return new StringToBoolConverter();
              }*/
        } else if (Date.class.isAssignableFrom(cellValueClazz)) {
            if (fieldClazz == String.class) {
                return new DateToStringConverter(fmt);
            } else if (fieldClazz == LocalDate.class) {
                return new DateAndLocalDateConverter();
            } else if (fieldClazz == LocalDateTime.class) {
                return new DateAndLocalDateTimeConverter();
            }
        }
        Optional<?> converterOpl = defaultReaderConverters.getConverter(cellValueClazz, fieldClazz);
        if (converterOpl.isPresent()) {
            return (Converter) converterOpl.get();
        }
        throw new ConvertException("不支持的转换" + cellValueClazz + " to " + fieldClazz);
    }

    /**
     * 获取属性转为字符串的默认转换器
     * 
     * @param valueClazz 要转换的数据类型
     * @param fmt
     * @return
     */
    public static <S> Converter<?,String> getDefaultWriteConverter(Class<S> valueClazz, String fmt) {
        // 数字如果有格式使用NumberToTextConverter,否则使用toString
        if (StringHelper.isNotEmpty(fmt)) {
            if (java.util.Date.class.isAssignableFrom(valueClazz)) {
                return new DateToStringConverter(fmt);
            } else if (Temporal.class.isAssignableFrom(valueClazz)) {
                return new TemporalToStringConverter(fmt);
            } else if (NumberHelper.isNumberType(valueClazz)) {
                DecimalFormat numFormator = new DecimalFormat(fmt);
                numFormator.setGroupingUsed(false);
                return new NumberToStringConverter(numFormator);
            } else if (valueClazz == Calendar.class) {
                return new CalendarToStringConverter(fmt);
            }
        } else {
            if (IIntKeyPair.class.isAssignableFrom(valueClazz)) {
                Optional<Converter<IIntKeyPair,String>> converterOpl = defaultWriterConverters
                        .getConverter(IIntKeyPair.class, String.class);
                if (converterOpl.isPresent()) {
                    return converterOpl.get();
                }
            } else {
                Optional<? extends Converter<S,String>> converterOpl = defaultWriterConverters.getConverter(valueClazz,
                        String.class);
                if (converterOpl.isPresent()) {
                    return converterOpl.get();
                }
            }
        }
        return defaultWriterConverters.getConverter(Object.class, String.class)
                .get();
    }

    /**
     * 读取excel单元格的值.读取出来的值类型只可能是:bool,number,date,string.
     * 
     * @param curCell 单元格cell
     * @return
     */
    public static Object getCellValue(Cell curCell) {
        if (curCell.getCellType() == CellType.BLANK || curCell.getCellType() == CellType.ERROR) {
            return null;
        } else if (curCell.getCellType() == CellType.BOOLEAN) {
            return curCell.getBooleanCellValue();
        } else if (curCell.getCellType() == CellType.NUMERIC) {
            if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(curCell)) {
                return curCell.getDateCellValue();
            } else {
                return curCell.getNumericCellValue();
            }

        } else {
            return curCell.getStringCellValue();
        }
    }

}
